package com.disthen.message.core;


import java.util.Collections;
import java.util.List;

import org.springframework.amqp.rabbit.config.BindingFactoryBean;
import org.springframework.amqp.rabbit.connection.CachingConnectionFactory;
import org.springframework.amqp.rabbit.listener.SimpleMessageListenerContainer;
import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanFactoryPostProcessor;
import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
import org.springframework.beans.factory.config.TypedStringValue;
import org.springframework.beans.factory.support.BeanDefinitionBuilder;
import org.springframework.beans.factory.support.DefaultListableBeanFactory;
import org.springframework.core.Ordered;

import com.disthen.message.core.config.Listeners;
import com.disthen.message.core.config.Template;
import com.disthen.message.core.util.NamespaceUtils;
import com.disthen.message.core.util.SimpleErrorHandler;

/**
 * 
 * @author Aster
 *	我一生无题 多情都不提
	看多画楼歌台景 惯多赋别离
	浮萍无踪迹 汲汲空浮名
	我愿醉后复醒 当垆仍是你
 */
public class MsgManager implements BeanFactoryPostProcessor ,Ordered{
	
	private final static String DEFAULT_HOST = "192.168.1.38";
	private final static String DEFAULT_PORT = "5672";
	private final static String DEFAULT_FACTORY_BEANID = "rabitConFact";
	private final static String exchangeBeanId = "rabitExchange";
	private final static String DEFAULT_ADMIN_BEANID = "rabbitAdim";
	private final static String DEFAULT_QUEUE = "monitor-quen2";
	private final static String DEFAULT_VIRHOST = "monitorPlatform";
	private final static String DEFAULT_EXCHANGE_TYPE= "topic";
	private final static String DEFAULT_EXCHANGE_NAME= "monitor-topic-exchange";

	private DefaultListableBeanFactory beanFactory; 
	
	private String adminBeanId = DEFAULT_ADMIN_BEANID;
	private String factoryBeanId = DEFAULT_FACTORY_BEANID;
	private String host = DEFAULT_HOST;
	private String port = DEFAULT_PORT;
	private String virhost = DEFAULT_VIRHOST;
	private String exchangeType = DEFAULT_EXCHANGE_TYPE;
	private String exchangeName = DEFAULT_EXCHANGE_NAME;
	private List<Listeners> listeners;
	
	private String username = "monitor";
	private String password = "12345";
	
	private List<Template> templates;
	
	
	public void setAdminBeanId(String adminBeanId) {
		this.adminBeanId = adminBeanId;
	}

	public void setFactoryBeanId(String factoryBeanId) {
		this.factoryBeanId = factoryBeanId;
	}

	public void setTemplates(List<Template> templates) {
		this.templates = templates;
	}

	public void setExchangeType(String exchangeType) {
		this.exchangeType = exchangeType;
	}

	public void setExchangeName(String exchangeName) {
		this.exchangeName = exchangeName;
	}
	
	public void setListeners(List<Listeners> listeners) {
		this.listeners = listeners;
	}

	public void setHost(String host) {
		this.host = host;
	}

	public void setPort(String port) {
		this.port = port;
	}

	public void setUsername(String username) {
		this.username = username;
	}

	public void setPassword(String password) {
		this.password = password;
	}

	public void setVirhost(String virhost) {
		this.virhost = virhost;
	}

	
	public void postProcessBeanFactory(
			ConfigurableListableBeanFactory beanFactory) throws BeansException {
		this.beanFactory = (DefaultListableBeanFactory) beanFactory;

		this.buildCnctFactory();
		
		this.buildAdmin();
		this.buildExchange();
		this.buildListeners();
		this.buildTemplate();
	}
	
	private void buildCnctFactory(){
		if(this.beanFactory.containsBean(factoryBeanId))
			return ;
		BeanDefinitionBuilder conectorfactory = BeanDefinitionBuilder.rootBeanDefinition(CachingConnectionFactory.class);
		conectorfactory.addPropertyValue("host", host);
		conectorfactory.addPropertyValue("port", port);
		conectorfactory.addPropertyValue("username", username);
		if(password != null && !password.isEmpty())
			conectorfactory.addPropertyValue("password", password);
		else conectorfactory.addPropertyValue("password", null);
		conectorfactory.addPropertyValue("virtualHost", virhost);
		
		this.beanFactory.registerBeanDefinition(factoryBeanId, conectorfactory.getBeanDefinition());
	}

	private void buildAdmin(){
		if(this.beanFactory.containsBean(adminBeanId))
			return ;
		BeanDefinitionBuilder rabbitAdmin = BeanDefinitionBuilder.rootBeanDefinition(org.springframework.amqp.rabbit.core.RabbitAdmin.class);
		rabbitAdmin.addConstructorArgReference(factoryBeanId);
		
		this.beanFactory.registerBeanDefinition(adminBeanId, rabbitAdmin.getBeanDefinition());
	}
	
	private void buildListeners(){
		
		if(listeners!=null&&!listeners.isEmpty()){
			for(Listeners key : listeners){
				if(key.getQueue() == null)
					key.setQueue(DEFAULT_QUEUE);
				if(key.getExchangeName() == null)
					key.setExchangeName(DEFAULT_EXCHANGE_NAME);
				else buildExchange(key);
				
				key.setQueueBeanRefName(buildQueues(key.getQueue()));
				
				bindExchange(key);
				
				BeanDefinitionBuilder lisn = BeanDefinitionBuilder.rootBeanDefinition(org.springframework.amqp.rabbit.listener.adapter.MessageListenerAdapter.class);
				
				lisn.addPropertyReference("delegate", key.getListener());
				if(key.getMethod() != null)
					lisn.addPropertyValue("defaultListenerMethod", key.getMethod());
				if(key.getMsgConverter() != null)
					lisn.addPropertyReference("messageConverter", key.getMsgConverter());
				if(key.getResponseExchange() != null && !key.getResponseExchange().isEmpty()){
					lisn.addPropertyValue("responseExchange", key.getResponseExchange());
					lisn.addPropertyValue("responseRoutingKey", key.getResponseKey());
				}
				this.beanFactory.registerBeanDefinition(key+"-lisn",lisn.getBeanDefinition());
				
				BeanDefinitionBuilder lisnCt = BeanDefinitionBuilder.rootBeanDefinition(SimpleMessageListenerContainer.class);
				lisnCt.addConstructorArgReference(factoryBeanId);
				lisnCt.addPropertyValue("queueNames", key.getQueue());
				lisnCt.addPropertyValue("defaultRequeueRejected", false);
				lisnCt.addPropertyValue("errorHandler", new SimpleErrorHandler());
				lisnCt.addPropertyReference("messageListener", key+"-lisn");
				this.beanFactory.registerBeanDefinition(key+"-listCt",lisnCt.getBeanDefinition());
			}
		}
		
	}
	private String buildQueues(String queue){
		BeanDefinitionBuilder que = BeanDefinitionBuilder.rootBeanDefinition(org.springframework.amqp.core.Queue.class);
		que.addConstructorArgValue(new TypedStringValue(queue));
		que.addConstructorArgValue(new TypedStringValue("true"));
		que.addConstructorArgValue(new TypedStringValue("true"));
		que.addConstructorArgValue(new TypedStringValue("true"));
		this.beanFactory.registerBeanDefinition(queue+"-bean", que.getBeanDefinition());
		return queue+"-bean";
	}
	
	private void bindExchange(Listeners para){
		BeanDefinitionBuilder builder = BeanDefinitionBuilder.genericBeanDefinition(BindingFactoryBean.class);
		builder.addPropertyValue("exchange", new TypedStringValue(para.getExchangeName()));
		if(NamespaceUtils.isNeedRoutingKey(para.getType()))
			builder.addPropertyValue("routingKey", para.getTopic());
		builder.addPropertyValue("arguments", Collections.<String, Object>emptyMap());
		builder.addPropertyReference("destinationQueue", para.getQueueBeanRefName());
		this.beanFactory.registerBeanDefinition(para.getQueue()+"-bind", builder.getBeanDefinition());
	}

	private void buildExchange(Listeners para){
		BeanDefinitionBuilder exchange = BeanDefinitionBuilder.genericBeanDefinition(NamespaceUtils.getExchange(para.getType()));
		exchange.addConstructorArgValue(new TypedStringValue(para.getExchangeName()));
		exchange.addConstructorArgValue(new TypedStringValue("true"));
		exchange.addConstructorArgValue(new TypedStringValue("false"));
		this.beanFactory.registerBeanDefinition(para.getExchangeName() + "bean", exchange.getBeanDefinition());
	}
	private void buildExchange(){
		if(this.beanFactory.containsBean(exchangeBeanId))
			return ;
		BeanDefinitionBuilder exchange = BeanDefinitionBuilder.genericBeanDefinition(NamespaceUtils.getExchange(exchangeType));
		exchange.addConstructorArgValue(new TypedStringValue(exchangeName));
		exchange.addConstructorArgValue(new TypedStringValue("true"));
		exchange.addConstructorArgValue(new TypedStringValue("false"));
		this.beanFactory.registerBeanDefinition(exchangeBeanId, exchange.getBeanDefinition());
	}
	
	private void buildTemplate(){
		if(templates != null && !templates.isEmpty()){
			for(Template tpl : templates){
				BeanDefinitionBuilder tplBuilder = BeanDefinitionBuilder.genericBeanDefinition(MessageTemplate.class);
				tplBuilder.addConstructorArgReference(factoryBeanId);
				tplBuilder.addPropertyValue("exchange", tpl.getExchangeName());
				if(tpl.getConverter() != null && !tpl.getConverter().isEmpty())
					tplBuilder.addPropertyReference("messageConverter", tpl.getConverter());
				this.beanFactory.registerBeanDefinition(tpl.getName(), tplBuilder.getBeanDefinition());
			}
		}
	}
	public int getOrder() {
		return 0;
	}
}
